package edu.ssdut.revise.models;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.github.javaparser.utils.Pair;
import edu.ssdut.revise.core.match.MatchStore;
import edu.ssdut.revise.core.match.TwoFileMatcher;
import edu.ssdut.revise.core.refactoringDetection.TwoFileRefactoringDetector;

import java.util.ArrayList;
import java.util.List;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class VisFragment {
    // must be serializable
    String repoIdentifier;
    String startCommitId;
    String endCommitId;

    String originalDir;
    String modifiedDir;

    List<PairWithDetector> cuPairsWithDetector = new ArrayList<>();


    public VisFragment(String repoIdentifier, String startCommitId, String endCommitId, MatchStore<CUWithInfo> cuWithInfoMatchStore) {
        this(cuWithInfoMatchStore);
        this.repoIdentifier = repoIdentifier;
        this.startCommitId = startCommitId;
        this.endCommitId = endCommitId;
    }

    public VisFragment(String originalDir, String modifiedDir, MatchStore<CUWithInfo> cuWithInfoMatchStore) {
        this(cuWithInfoMatchStore);
        this.originalDir = originalDir;
        this.modifiedDir = modifiedDir;
    }

    private VisFragment(MatchStore<CUWithInfo> cuWithInfoMatchStore) {
        // detach matched and unmatched and wrap into fragments
        for (CUWithInfo deletedCu : cuWithInfoMatchStore.getUnmatchedNodes1()) {
            // deleted file
            this.insertPair(new Pair<>(deletedCu, null), null);
        }
        for (CUWithInfo addedCu : cuWithInfoMatchStore.getUnmatchedNodes2()) {
            // added file
            this.insertPair(new Pair<>(null, addedCu), null);
        }
        for (Pair<CUWithInfo, CUWithInfo> pair : cuWithInfoMatchStore.getMatched()) {
            // matched pair
            TwoFileMatcher matcher = new TwoFileMatcher(pair.a, pair.b);
            // refactorings detect
            TwoFileRefactoringDetector detector = new TwoFileRefactoringDetector(matcher);
            detector.detect();
            this.insertPair(new Pair<>(pair.a, pair.b), detector);
        }
    }

    /**
     * insert result into the final fragment, note that the detector should have already called the detector method
     *
     * @param pair     a pair of CUWithInfo
     * @param detector detector containing all the refactoring results
     */
    public void insertPair(Pair<CUWithInfo, CUWithInfo> pair, TwoFileRefactoringDetector detector) {
        // note that the detector should have already called the detect method
        this.cuPairsWithDetector.add(new PairWithDetector(pair, detector));
    }

    public String getRepoIdentifier() {
        return repoIdentifier;
    }

    public String getStartCommitId() {
        return startCommitId;
    }

    public String getEndCommitId() {
        return endCommitId;
    }

    public List<PairWithDetector> getCuPairsWithDetector() {
        return cuPairsWithDetector;
    }

    public String getOriginalDir() {
        return originalDir;
    }

    public String getModifiedDir() {
        return modifiedDir;
    }

    //TODO make this class serializable
    private static class PairWithDetector {
        Pair<VisCUNode, VisCUNode> pair;
        TwoFileRefactoringDetector detector;

        public PairWithDetector(Pair<CUWithInfo, CUWithInfo> pair, TwoFileRefactoringDetector detector) {
            VisCUNode n1 = null, n2 = null;
            if (pair.a != null) {
                n1 = new VisCUNode(pair.a);
            }
            if (pair.b != null) {
                n2 = new VisCUNode(pair.b);
            }
            this.pair = new Pair<>(n1, n2);
            this.detector = detector;
        }

        public Pair<VisCUNode, VisCUNode> getPair() {
            return pair;
        }

        public TwoFileRefactoringDetector getDetector() {
            return detector;
        }

    }
}
